﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Linq.Expressions;
using System.Web.UI.WebControls;
using System.Reflection;


    public static class SortHelper
    {

        /// <summary>
        /// Funktion när man av någon anledning inte kan använda OrderBy (this IQueryable source, string propertyName, bool descending, bool anotherLevel)
        /// I typo skickar man in typen av objekten i source
        /// </summary>
        /// <param name="source"></param>
        /// <param name="typo"></param>
        /// <param name="propertyName"></param>
        /// <param name="descending"></param>
        /// <param name="anotherLevel"></param>
        /// <returns></returns>
        public static IOrderedQueryable OrderBy(this IQueryable source, object typo, string propertyName, bool descending, bool anotherLevel)
        {

            ParameterExpression param = Expression.Parameter(typo.GetType(), string.Empty); // I don't care about some naming
            MemberExpression property = Expression.PropertyOrField(param, propertyName);
            LambdaExpression sort = Expression.Lambda(property, param);

            MethodCallExpression call = Expression.Call(
                typeof(Queryable),
                (!anotherLevel ? "OrderBy" : "ThenBy") + (descending ? "Descending" : string.Empty),
                new[] { typo.GetType(), property.Type },
                source.Expression,
                Expression.Quote(sort));

            return (IOrderedQueryable)source.Provider.CreateQuery(call);
        }
        /// <summary>
        /// För alla typer/otypade IQueryable som är genererat av LINQ ex alla typer av frågor mot Entity
        /// </summary>
        /// <param name="source"></param>
        /// <param name="propertyName"></param>
        /// <param name="descending"></param>
        /// <param name="anotherLevel"></param>
        /// <returns></returns>
        public static IOrderedQueryable OrderBy(this IQueryable source, string propertyName, bool descending, bool anotherLevel)
        {
            Type T = source.ElementType;
            Type selectorResultType;

            LambdaExpression sort = GenerateSelector(propertyName, T, out selectorResultType);
            
            MethodCallExpression call = Expression.Call(
                typeof(Queryable),
                (!anotherLevel ? "OrderBy" : "ThenBy") + (descending ? "Descending" : string.Empty),
                new[] { T, selectorResultType},
                source.Expression,
                Expression.Quote(sort)
               );

           
            return (IOrderedQueryable) source.Provider.CreateQuery(call);
        }

        private static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName, bool descending, bool anotherLevel)
        {
            Type type = typeof(T);
            Type selectorResultType;
            LambdaExpression sort = GenerateSelector<T>(propertyName, out selectorResultType);

            MethodCallExpression call = Expression.Call(
                typeof(Queryable),
                (!anotherLevel ? "OrderBy" : "ThenBy") + (descending ? "Descending" : string.Empty),
                new[] { typeof(T), selectorResultType},
                source.Expression,
                Expression.Quote(sort));

            return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(call);
        }
        
       
        private static LambdaExpression GenerateSelector<T>(String propertyName, out Type resultType)
        {
            // Create a parameter to pass into the Lambda expression (Entity => Entity.OrderByField). 
            var parameter = Expression.Parameter(typeof(T), "Entity");
            //  create the selector part, but support child properties 
            PropertyInfo property;
            Expression propertyAccess;
            if (propertyName.Contains('.'))
            {
                // support to be sorted on child fields. 
                String[] childProperties = propertyName.Split('.');
                property = typeof(T).GetProperty(childProperties[0]);
                propertyAccess = Expression.MakeMemberAccess(parameter, property);
                for (int i = 1; i < childProperties.Length; i++)
                {
                    //property = property.PropertyType.GetProperty(childProperties[i]);
                    //propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
                    Type t = property.PropertyType;
                    if (!t.IsGenericType)
                    {
                        property = t.GetProperty(childProperties[i]);
                    }
                    else
                    {
                        property = t.GetGenericArguments().First().GetProperty(childProperties[i]);
                    }
                    propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
                }
            }
            else
            {
                property = typeof(T).GetProperty(propertyName);
                propertyAccess = Expression.MakeMemberAccess(parameter, property);
            }
            resultType = property.PropertyType;
            // Create the order by expression. 
            return Expression.Lambda(propertyAccess, parameter);
        } 

        private static LambdaExpression GenerateSelector(String propertyName, Type T, out Type resultType) 
        {
            // Create a parameter to pass into the Lambda expression (Entity => Entity.OrderByField). 
            var parameter = Expression.Parameter(T, "Entity");
            //  create the selector part, but support child properties 
            PropertyInfo property;
            Expression propertyAccess;
            if (propertyName.Contains('.'))
            {
                // support to be sorted on child fields. 
                String[] childProperties = propertyName.Split('.');
                property = T.GetProperty(childProperties[0]);
                propertyAccess = Expression.MakeMemberAccess(parameter, property);
                for (int i = 1; i < childProperties.Length; i++)
                {
                    //property = property.PropertyType.GetProperty(childProperties[i]);
                    //propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
                    Type t = property.PropertyType;
                    if (!t.IsGenericType)
                    {
                        property = t.GetProperty(childProperties[i]);
                    }
                    else
                    {
                        property = t.GetGenericArguments().First().GetProperty(childProperties[i]);
                    }


                    propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
   
                }
            }
            else
            {
                property = T.GetProperty(propertyName);
                propertyAccess = Expression.MakeMemberAccess(parameter, property);
                
            }           
            resultType = property.PropertyType;
            
            // Create the order by expression. 
            return Expression.Lambda(propertyAccess, parameter);
        } 


        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName)
        {
            return OrderBy(source, propertyName, false, false);
        }

        public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string propertyName)
        {
            return OrderBy(source, propertyName, true, false);
        }

        public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string propertyName)
        {
            return OrderBy(source, propertyName, false, true);
        }

        public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string propertyName)
        {
            return OrderBy(source, propertyName, true, true);
        }

        public static void ReSort(this ListView lv, System.Web.UI.StateBag viewState)
        {

            if (viewState["CurrentSortExpression"] != null && viewState["CurrentSortDirection"] != null)
            {
                IQueryable IQ = null;

                if (lv.DataSource is System.Collections.ICollection)
                {
                    IQ = ((System.Collections.ICollection)lv.DataSource).AsQueryable();
                    IQ = IQ.OrderBy(viewState["CurrentSortExpression"].ToString(), ((SortDirection)viewState["CurrentSortDirection"]) == SortDirection.Descending, false);
                    lv.DataSource = (System.Collections.ICollection)(from Object O in IQ select O).ToArray(); 
                    lv.DataBind();
                }
                else
                {
                    if (lv.DataSource is System.Collections.IList)
                    {
                        IQ = ((System.Collections.IList)lv.DataSource).AsQueryable();
                    }
                    else if (lv.DataSource is System.Collections.IEnumerable)
                    {
                        IQ = ((System.Collections.IEnumerable)lv.DataSource).AsQueryable();
                    }
                    else
                    {
                        IQ = (IQueryable)lv.DataSource;
                    }
               

                lv.DataSource = IQ.OrderBy(viewState["CurrentSortExpression"].ToString(), ((SortDirection)viewState["CurrentSortDirection"]) == SortDirection.Descending, false);
                lv.DataBind();
                }
            }
        }

        public static void ReSort(this GridView gr , System.Web.UI.StateBag viewState)
        {

            if (viewState["CurrentSortExpression"] != null && viewState["CurrentSortDirection"] != null)
            {
                IQueryable IQ = null;


                if (gr.DataSource is System.Collections.ICollection)
                {
                    IQ = ((System.Collections.ICollection)gr.DataSource).AsQueryable();
                    IQ = IQ.OrderBy(viewState["CurrentSortExpression"].ToString(), ((SortDirection)viewState["CurrentSortDirection"]) == SortDirection.Descending, false);
                    gr.DataSource = (System.Collections.ICollection)(from Object O in IQ select O).ToArray();
                    gr.DataBind();
                }
                else
                {
                    if (gr.DataSource is System.Collections.IList)
                    {
                        IQ = ((System.Collections.IList)gr.DataSource).AsQueryable();
                    }
                    else if (gr.DataSource is System.Collections.IEnumerable)
                    {
                        IQ = ((System.Collections.IEnumerable)gr.DataSource).AsQueryable();
                    }
                    else
                    {
                        IQ = (IQueryable)gr.DataSource;
                    }

                    gr.DataSource = IQ.OrderBy(viewState["CurrentSortExpression"].ToString(), ((SortDirection)viewState["CurrentSortDirection"]) == SortDirection.Descending, false);
                    gr.DataBind();
                }
            }
        }
     
        public static void ListViewOrder(this ListView lv, System.Web.UI.StateBag viewState, System.Web.UI.WebControls.ListViewSortEventArgs e)
        {



            if (viewState[e.SortExpression + "_SortDirection"] == null)
                viewState[e.SortExpression + "_SortDirection"] = SortDirection.Descending;


            if (((SortDirection)viewState[e.SortExpression + "_SortDirection"]) == SortDirection.Descending)
                e.SortDirection = SortDirection.Ascending;
            else
                e.SortDirection = SortDirection.Descending;


            viewState[e.SortExpression + "_SortDirection"] = e.SortDirection;

            IQueryable IQ = null;

            if (lv.DataSource is System.Collections.ICollection)
            {
                IQ = ((System.Collections.ICollection)lv.DataSource).AsQueryable();
                IQ = IQ.OrderBy(e.SortExpression, e.SortDirection == SortDirection.Descending, false);
                lv.DataSource = (System.Collections.ICollection)(from Object O in IQ select O).ToArray(); 
                lv.DataBind();
            }
            else
            {
                if (lv.DataSource is System.Collections.IList)
                {
                    IQ = ((System.Collections.IList)lv.DataSource).AsQueryable();
                }
                else if (lv.DataSource is System.Collections.IEnumerable)
                {
                    IQ = ((System.Collections.IEnumerable)lv.DataSource).AsQueryable();
                }
                else
                {
                    IQ = (IQueryable)lv.DataSource;
                }

                lv.DataSource = IQ.OrderBy(e.SortExpression, e.SortDirection == SortDirection.Descending, false);
                lv.DataBind();
            }

            viewState["CurrentSortExpression"] = e.SortExpression;
            viewState["CurrentSortDirection"] = e.SortDirection;

        }

        public static void GridViewOrder(this GridView gr, System.Web.UI.StateBag viewState, System.Web.UI.WebControls.GridViewSortEventArgs e)
        {

            if (viewState[e.SortExpression + "_SortDirection"] == null)
                viewState[e.SortExpression + "_SortDirection"] = SortDirection.Descending;


            if (((SortDirection)viewState[e.SortExpression + "_SortDirection"]) == SortDirection.Descending)
                e.SortDirection = SortDirection.Ascending;
            else
                e.SortDirection = SortDirection.Descending;


            viewState[e.SortExpression + "_SortDirection"] = e.SortDirection;

            IQueryable IQ = null;

            if (gr.DataSource is System.Collections.ICollection)
            {
                IQ = ((System.Collections.ICollection)gr.DataSource).AsQueryable();
                IQ = IQ.OrderBy(e.SortExpression, e.SortDirection == SortDirection.Descending, false);
                gr.DataSource = (System.Collections.ICollection)(from Object O in IQ select O).ToArray();
                gr.DataBind();
            }
            else
            {


                if (gr.DataSource is System.Collections.IList)
                {
                    IQ = ((System.Collections.IList)gr.DataSource).AsQueryable();
                }
                else if (gr.DataSource is System.Collections.IEnumerable)
                {
                    IQ = ((System.Collections.IEnumerable)gr.DataSource).AsQueryable();
                }
                else
                {
                    IQ = (IQueryable)gr.DataSource;
                }

                gr.DataSource = IQ.OrderBy(e.SortExpression, e.SortDirection == SortDirection.Descending, false);
                gr.DataBind();
            }

            viewState["CurrentSortExpression"] = e.SortExpression;
            viewState["CurrentSortDirection"] = e.SortDirection;

        }




        #region exemepel
        //Orderby exempel på subtables
        //public static IQueryable OrderBy<TEntity>(this IQueryable source, string orderByValues) where TEntity : class
        //{
        //    IQueryable returnValue = null;

        //    string orderPair = orderByValues.Trim().Split(',')[0];
        //    string command = orderPair.ToUpper().Contains("DESC") ? "OrderByDescending" : "OrderBy";
        //    var type = typeof(TEntity);
        //    var parameter = Expression.Parameter(type, "p");

        //    string propertyName = (orderPair.Split(' ')[0]).Trim();
        //    PropertyInfo property;
        //    MemberExpression propertyAccess;
        //    if (propertyName.Contains('.'))
        //    {
        //        // support to be sorted on child fields.  
        //        String[] childProperties = propertyName.Split('.');
        //        property = typeof(TEntity).GetProperty(childProperties[0]);
        //        propertyAccess = Expression.MakeMemberAccess(parameter, property);
        //        for (int i = 1; i < childProperties.Length; i++)
        //        {
        //            Type t = property.PropertyType;
        //            if (!t.IsGenericType)
        //            {
        //                property = t.GetProperty(childProperties[i]);
        //            }
        //            else
        //            {
        //                property = t.GetGenericArguments().First().GetProperty(childProperties[i]);
        //            }
        //            propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
        //        }
        //    }
        //    else
        //    {
        //        property = type.GetProperty(propertyName);
        //        propertyAccess = Expression.MakeMemberAccess(parameter, property);
        //    }

        //    var orderByExpression = Expression.Lambda(propertyAccess, parameter);
        //    var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },source.Expression, Expression.Quote(orderByExpression));

        //    returnValue = source.Provider.CreateQuery<TEntity>(resultExpression);


        //    if (orderByValues.Trim().Split(',').Count() > 1)
        //    {
        //        // remove first item 
        //        string newSearchForWords = orderByValues.ToString().Remove(0, orderByValues.ToString().IndexOf(',') + 1);
        //        returnValue = source.OrderBy(newSearchForWords);
        //    }

        //    return returnValue;

        //}
        //private static LambdaExpression GenerateSelector<TEntity>(String propertyName, out Type resultType) where TEntity : class
        //{
        //    // Create a parameter to pass into the Lambda expression (Entity => Entity.OrderByField). 
        //    var parameter = Expression.Parameter(typeof(TEntity), "Entity");
        //    //  create the selector part, but support child properties 
        //    PropertyInfo property;
        //    Expression propertyAccess;
        //    if (propertyName.Contains('.'))
        //    {
        //        // support to be sorted on child fields. 
        //        String[] childProperties = propertyName.Split('.');
        //        property = typeof(TEntity).GetProperty(childProperties[0]);
        //        propertyAccess = Expression.MakeMemberAccess(parameter, property);
        //        for (int i = 1; i < childProperties.Length; i++)
        //        {
        //            //property = property.PropertyType.GetProperty(childProperties[i]);
        //            //propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
        //            Type t = property.PropertyType;
        //            if (!t.IsGenericType)
        //            {
        //                property = t.GetProperty(childProperties[i]);
        //            }
        //            else
        //            {
        //                property = t.GetGenericArguments().First().GetProperty(childProperties[i]);
        //            }
        //            propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
        //        }
        //    }
        //    else
        //    {
        //        property = typeof(TEntity).GetProperty(propertyName);
        //        propertyAccess = Expression.MakeMemberAccess(parameter, property);
        //    }
        //    resultType = property.PropertyType;
        //    // Create the order by expression. 
        //    return Expression.Lambda(propertyAccess, parameter);
        //} 

        //private static MethodCallExpression GenerateMethodCall<TEntity>(IQueryable<TEntity> source, string methodName, String fieldName) where TEntity : class
        //{
        //    Type type = typeof(TEntity);
        //    Type selectorResultType;
        //    LambdaExpression selector = GenerateSelector<TEntity>(fieldName, out selectorResultType);
        //    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName,
        //                                    new Type[] { type, selectorResultType },
        //                                    source.Expression, Expression.Quote(selector));
        //    return resultExp;
        //}

        //private static IOrderedQueryable<T> OrderingHelper<T>(IQueryable<T> source, string propertyName, bool descending, bool anotherLevel)
        //{
        //    ParameterExpression param = Expression.Parameter(typeof(T), string.Empty); // I don't care about some naming
        //    MemberExpression property = Expression.PropertyOrField(param, propertyName);
        //    LambdaExpression sort = Expression.Lambda(property, param);

        //    MethodCallExpression call = Expression.Call(
        //        typeof(Queryable),
        //        (!anotherLevel ? "OrderBy" : "ThenBy") + (descending ? "Descending" : string.Empty),
        //        new[] { typeof(T), property.Type },
        //        source.Expression,
        //        Expression.Quote(sort));

        //    return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(call);
        //}

        #endregion
    }
